home *** CD-ROM | disk | FTP | other *** search
/ Gold Medal Software 3 / Gold Medal Software - Volume 3 (Gold Medal) (1994).iso / graphics / povsuds.arj / POVSUDS.C < prev    next >
C/C++ Source or Header  |  1993-12-16  |  33KB  |  1,084 lines

  1. /**************
  2. Program: POVSUDS.C          (Use medium memory model)
  3. Usage: POVSUDS output.dat
  4.        The screen will turn blue and the speaker will beep when the
  5.        program has completed its full compliment of spheres.
  6.  
  7.         Basic algorithm from "Computers, Pattern, Chaos, and Beauty" by
  8.         Clifford Pickover (St. Martin's Press).
  9.         Program by Sam Hobbs, C User's Journal, Sept '91
  10.  
  11. Purpose:
  12.         Draws "osculating" tangent circles of random sizes in two dimensions.
  13.         Outputs raytracer data in the form of user definable objects.
  14.  
  15. Modification History:
  16. -----------------------
  17.      11/23/93 Eduard [esp] Schwan
  18.          o Updated Dan's updates to support Macintosh models:
  19.          o Uncluttered #ifdefs with IBMPC_DISPLAY & MAC_DISPLAY IDs
  20.          o No-opped gotoxy for Mac
  21.          o Added the write_pov_textures function
  22.          o Removed commas from 1.0 object syntax output (write_pov_object)
  23.          o Changed #define VERBOSE into command-line option be_verbose variable
  24.  
  25.      11/20/93 Dan Farmer Version 2.0 (IBM version)
  26.          o Converted to POV-Ray 2.0.
  27.          o Made POV-Ray 2.0 the default ray tracer.
  28.          o Put default ray tracer first on menu.
  29.          o Dropped POV-Ray 0.5 support.
  30.          o Reworked sample camera file.
  31.          o Somewhere along the way, I added a color fill to the preview
  32.            spheres (for the PC version only).
  33.  
  34.      03/09/93 Eduard [esp] Schwan
  35.          o Added code to support Macintosh implementation, more generic
  36.          o Added minimal command-line parameter code
  37.          o Added missing suds texture IDs, changed sudsNNN to sudsTexNNN for clarity
  38.          o Cosmetics on source code, added function separators, made static.
  39.          o Allocate humongo circle array dynamically, not as static array.
  40.          o Fixed bug that left some spheres out in space, not touching.
  41.          o Fixed syntax for POV 0.5, 1.0, 2.0.
  42.          o Added user-configurable random distribution functions
  43.  
  44.      01/16/93 dmf  Add POV-Ray 1.5 support
  45.      01/05/92 Dan Farmer
  46.          o Remove MAX_RADIUS_DIVISOR and replace with simple rmax value.
  47.  
  48.      01/02/92 Tim Wegner
  49.          o Changed to support new POV-Ray {} syntax
  50.          o Added registerVGA call to link with egavga.obj
  51.          o Made errors go to stderr
  52.          o Added 3D support
  53.          o Cleaned up input screen
  54.          o Added multiple textures
  55.          o Added spherical and rectangular bounding options
  56.          o Output to command line parameter
  57.          o Home the screen after <A>gain
  58.          o Remove POV-Ray Noise and Turbulence variations
  59.  
  60.      11/16/91 Dan Farmer
  61.          o Added POV-Ray raytrace output.
  62.          o (Uses printf() with redirection to create output include file.)
  63.          o Track object extents and auto-center object.
  64.          o Get user input for variable parameters.
  65.          o Add z to circle_type structure.  (Not used yet.)
  66.          o Add POV Noise and Turbulence variations
  67. ****************/
  68.  
  69.  
  70. /*-----------------------------------------------------------------------*/
  71.  
  72. #if defined (__TURBOC__) || defined (__BORLANDC__)
  73. #define IBMPC_DISPLAY    1
  74. #endif
  75.  
  76. #if defined(applec) || defined(THINK_C)
  77. #define MAC_DISPLAY    1
  78. #endif
  79.  
  80. /*==== ANSI C Library headers ====*/
  81. #include <stdlib.h>        /* malloc, atof, etc. */
  82. #include <stdio.h>
  83. #include <string.h>        /* strcat, etc. */
  84. #include <math.h>
  85. #include <time.h>       /* required for random function */
  86.  
  87. #if defined (IBMPC_DISPLAY)
  88. #include <conio.h>
  89. #include <graphics.h>
  90. #include <dos.h>
  91. #endif
  92.  
  93. #if defined(MAC_DISPLAY)
  94. // if using Macintosh, include user interface stuff
  95. #include "POVSuds.mac.h"
  96. #else
  97. // This is usually a no-op for non-Mac Systems
  98. #define COOPERATE exit_if_kbhit();
  99. #endif
  100.  
  101.  
  102. /*-----------------------------------------------------------------------*/
  103.  
  104. #define VERSION        "2.0a"
  105.  
  106. #if defined (IBMPC_DISPLAY)
  107.  
  108. #define CIRCLE(x,y,r)            circle(x,y,r)
  109.  
  110. #define INIT_GRAPHICS()            init_graphics()
  111. #define SETBKCOLOR(n)            setbkcolor(n)
  112. #define CLOSEGRAPH()            closegraph()
  113. #define RECTANGLE(x1,y1,x2,y2)    rectangle(x1,y1,x2,y2)
  114. #define CLEARDEVICE()            cleardevice()
  115. #define GOTO_XY(x,y)            gotoxy(x,y)
  116. #else
  117. // Mac doesn't use these!
  118. #define CIRCLE(x,y,r)
  119. #define INIT_GRAPHICS()
  120. #define SETBKCOLOR(n)
  121. #define CLOSEGRAPH()
  122. #define RECTANGLE(x1,y1,x2,y2)
  123. #define CLEARDEVICE()
  124. #define GOTO_XY(x,y)
  125. #endif
  126.  
  127. #if !defined(FALSE)
  128. #define FALSE 0
  129. #define TRUE !FALSE
  130. #endif
  131.  
  132. #if defined (IBMPC_DISPLAY)
  133. #define RANDOMIZE() randomize()
  134. #define RAND() rand()
  135. // RAND_MAX is defined in the ANSI <stdlib.h>, hopefully Borland complies/compiles?
  136. // #define RAND_MAX 32767
  137. #else
  138. #if defined(applec) || defined(THINK_C)
  139. #define SEED()    ((int)TickCount())
  140. #endif
  141. /* ANSI Standard random calls */
  142. #define RANDOMIZE() srand(SEED())
  143. #define RAND() rand()
  144. #endif
  145.  
  146. #define RANDOM() DoDistribution()
  147.  
  148. #if !defined(DBL)
  149. #define DBL double
  150. #endif
  151.  
  152. #define MIN_RADIUS 0.01
  153. #define MAX_RADIUS 75.0        /* dmf: Scrap the MAX_RADIUS_DIVISOR */
  154. #define MIN_CIRCLES 1
  155. #define MAX_CIRCLES 5000       /* dmf: Increased max */
  156.  
  157. #define OLD        1                /* Bounds style */
  158. #define BOX        2
  159. #define SPHERE    3
  160.  
  161. #define POV20    1
  162. #define POV10    2
  163. #define RAY_MAX        POV10
  164.  
  165. #define DIST_EVEN        1
  166. #define DIST_GAUSS5        2
  167. #define DIST_GAUSS10    3
  168. #define DIST_MAX        DIST_GAUSS10
  169.  
  170.  
  171. /*-----------------------------------------------------------------------*/
  172. int    main(int argc, char *argv[]);
  173. static void    generate_circles(void);
  174. static DBL  new_radius(DBL x, DBL y, DBL z);
  175. static DBL  distance(DBL x, DBL y, DBL z, int i);
  176. static void    get_cmdline_args(int argc, char **argv);
  177. #if defined (IBMPC_DISPLAY)
  178. static void get_user_input(void);
  179. static void exit_if_kbhit(void);
  180. static void gerror(int errorcode);
  181. static void init_graphics(void);
  182. static void movetoxy(int x, int y);
  183. int sort_function( const void *a, const void *b);
  184. #endif
  185. static void write_pov_data(void);
  186. static void write_pov_object(DBL x, DBL y, DBL z, DBL r, int t);
  187. static void write_pov_sudsobj(void);
  188. static void write_pov_textures(int num_tex);
  189. static DBL  GaussDistribution(int n);
  190. static DBL  EvenDistribution(void);
  191. static DBL  DoDistribution(void);
  192.  
  193. #if !defined (min)
  194. static DBL min (DBL value1, DBL value2);
  195. static DBL max (DBL value1, DBL value2);
  196. DBL min(DBL value1, DBL value2)
  197. {
  198.    return ( (value1 < value2) ? value1 : value2);
  199. }
  200. DBL max(DBL value1, DBL value2)
  201. {
  202.    return ( (value1 > value2) ? value1 : value2);
  203. }
  204. #endif
  205.  
  206.  
  207. /*-----------------------------------------------------------------------*/
  208. typedef struct {
  209.     DBL x;
  210.     DBL y;
  211.     DBL z;
  212.     DBL r;
  213.     } circle_type;
  214.  
  215.  
  216. /*-----------------------------------------------------------------------*/
  217. static    circle_type *c = NULL;
  218. static    int num_circles = 200;
  219. static    DBL min_radius = MIN_RADIUS;
  220. static    DBL max_radius =  MAX_RADIUS;
  221. static    short    bailout_max = 12000;
  222. static    int N;
  223. static    int num_textures = 1;
  224. static    int raytracer_kind = POV20;
  225. static    int distMethod    = DIST_EVEN;
  226. static    DBL xmax, ymax; /* currently set to screen coordinates in get_user_input() */
  227. static    DBL zmax = 0.0;
  228. static    DBL rmax;
  229. static    int constrain = OLD;
  230. static    int be_verbose = FALSE;
  231. static    FILE *fpout;
  232. static char *tracers[RAY_MAX] =
  233. {
  234.     "POV-Ray v2.0",
  235.     "POV-Ray v1.0"
  236. };
  237. static char *disttypes[DIST_MAX] =
  238. {
  239.     "Even (Random) Distribution",
  240.     "Gaussian Distribution,  5 samples",
  241.     "Gaussian Distribution, 10 Samples"
  242. };
  243. static char *begComm[RAY_MAX] = {"//","//"};    // begin/end comments for each tracer syntax
  244. static char *endComm[RAY_MAX] = {"",""};
  245. static char *bounds[] =
  246. {
  247.     "centers constrained to rectangular region",
  248.     "objects constrained to rectangular region",
  249.     "objects constrained to spherical region"
  250. };
  251. #define NUM_BOUNDS (sizeof(bounds)/sizeof(char *))
  252.  
  253.  
  254. /*-----------------------------------------------------------------------*/
  255. int main(int argc, char *argv[])
  256. {
  257. #if defined (IBMPC_DISPLAY)
  258.     int keypress, done;
  259. #endif
  260.  
  261.     c = (circle_type *)malloc((MAX_CIRCLES+1)*sizeof(circle_type));
  262.  
  263. #if defined (IBMPC_DISPLAY)
  264.     if (argc != 2)
  265.     {
  266.        printf("\nPOV Suds\n  Usage: POVSUDS <outfile>\n");
  267.        exit(1);
  268.     }
  269.  
  270.     if ((fpout = fopen(argv[1],"w")) == NULL)
  271.     {
  272.        printf("## Error!  Can't open %s ... aborting\n",argv[1]);
  273.        exit(1);
  274.     }
  275.  
  276.     done = FALSE;
  277.  
  278.     INIT_GRAPHICS();
  279.     do {
  280.         SETBKCOLOR(0);                                  /* color to black */
  281.         CLEARDEVICE();
  282.         get_user_input();
  283.  
  284.         CLEARDEVICE();
  285.         generate_circles();
  286.  
  287.         // Redisplay in z sorted order, colored by r
  288.         qsort( (void *)c, num_circles,sizeof(c[3]), sort_function);
  289.         for (N = 0; N < num_circles; N++) {
  290.             setfillstyle(SOLID_FILL, (int) c[N].r % getmaxcolor() +1);
  291.             //pieslice(c[N].x, c[N].y, 0,360, c[N].r);
  292.             fillellipse(c[N].x, c[N].y, c[N].r, c[N].r);
  293.         }
  294.  
  295.  
  296.         /* Frame the screen to show user that program is finished */
  297.         GOTO_XY(1,3);
  298.         fputs("\007", stdout);                                 /* beep */
  299.         fputs("<W>rite <Q>uit <R>edo",stdout);
  300.         RECTANGLE(0, 0, getmaxx(), getmaxy());             /* draw frame */
  301.  
  302.         keypress = getch();                             /* wait for keypress */
  303.         switch (keypress)
  304.         {
  305.         case 'R':
  306.         case 'r':
  307.             done = FALSE;
  308.             break;
  309.         case 'W':
  310.         case 'w':
  311.             write_pov_data();                           /* write PoV data */
  312.             CLOSEGRAPH();
  313.             return(0);                           /* quit */
  314.         case 'Q':
  315.         case 'q':
  316.             done = TRUE;
  317.             break;
  318.         default:
  319.             break;
  320.         }
  321.     } while (!done);
  322.  
  323.     CLOSEGRAPH();
  324. #else
  325.         // The Mac way...
  326.         get_cmdline_args(argc, argv);
  327.         generate_circles();
  328.         write_pov_data();                           /* write PoV data */
  329.         if ((fpout) && (fpout != stdout))
  330.             fclose(fpout);
  331. #endif
  332.  
  333.     if (c != NULL)
  334.         free(c);
  335.     return 0;
  336. } // main
  337.  
  338.  
  339. /*-----------------------------------------------------------------------*/
  340. static void generate_circles()
  341. {
  342.     DBL  x_pos, y_pos, z_pos, radius;
  343.     short    bailout;
  344.  
  345.     RANDOMIZE();
  346.  
  347.     /* Note that xmax and ymax would probably be better for raytrace
  348.        output if defined as a radius instead of screen rez.  This would
  349.        clump the circles into a larger circular area.    */
  350.  
  351.     /* constrain to this sphere - tucked safely at the end of c[] */
  352.     c[MAX_CIRCLES].x = xmax/2;
  353.     c[MAX_CIRCLES].y = ymax/2;
  354.     c[MAX_CIRCLES].z = zmax/2;
  355.     c[MAX_CIRCLES].r = min(xmax,ymax) * 0.5;
  356.  
  357.     /* draw the rest of the circles */
  358.     for (N = 0; N < num_circles ; N++)
  359.     {
  360.         bailout = 0;
  361.         do {
  362.             x_pos = RANDOM() * xmax;
  363.             y_pos = RANDOM() * ymax;
  364.             if(zmax > 0.0)
  365.                 z_pos = RANDOM() * zmax;
  366.             else
  367.                 z_pos = 0.0;
  368.             radius = new_radius(x_pos, y_pos, z_pos);
  369.             if (bailout_max > 0)
  370.                 bailout++;
  371.             if ((bailout & 31) == 31) // breathe every 32 times through
  372.                 COOPERATE
  373.         } while ( ((radius < min_radius) || (radius > max_radius)) && (bailout < bailout_max) );
  374.  
  375.         if (bailout >= bailout_max)
  376.         {
  377.           printf("\nERROR! couldn't place circle #%d after %d tries... giving up.\n", N, bailout);
  378.           num_circles = N; /* Set a new lower # circles! */
  379.           break; /* out of for loop */
  380.         }
  381.         else
  382.         {
  383.           CIRCLE(x_pos,y_pos, radius);
  384.  
  385.           c[N].x = x_pos;
  386.           c[N].y = y_pos;
  387.           c[N].z = z_pos;
  388.           c[N].r = radius;
  389.           GOTO_XY(1,1);
  390.           if(be_verbose)
  391.             printf("%4d Units: sphere { <%7.3g, %7.3g, %7.3g>, %7.3g } \r",N,x_pos,y_pos,z_pos,radius);
  392.           else
  393.                printf("%4d Units\r",N);
  394.         }
  395.     }
  396. } // generate_circles
  397.  
  398.  
  399. /*-----------------------------------------------------------------------*/
  400. /* Function: new_radius(x,y,z)
  401.    Purpose:
  402.    Returns the distance from the point x,y,z to the nearest circle.
  403.    Aborts and returns the calculated negative or zero distance if x,y
  404.    lies on or inside of another circle
  405. */
  406.  
  407. static DBL new_radius(DBL x, DBL y, DBL z)
  408. {
  409.     int i, touched;
  410.     DBL    radius, dist_circle;
  411.  
  412.     radius = max_radius;
  413.  
  414.     switch (constrain)
  415.     {
  416.     case OLD:
  417.         break;
  418.  
  419.     case BOX:
  420.         /* don't let get outside box */
  421.         if (radius > x)
  422.             radius = x;
  423.         if (radius > xmax - x)
  424.            radius = xmax - x;
  425.         if (radius > y)
  426.            radius = y;
  427.         if (radius > ymax - y)
  428.            radius = ymax - y;
  429.         if (zmax > min_radius*2)
  430.         {
  431.             if (radius > z)
  432.                 radius = z;
  433.             if (radius > zmax-z)
  434.                 radius = zmax-z;
  435.         }
  436.         break;
  437.  
  438.     case SPHERE:
  439.        dist_circle = distance(x,y,z,MAX_CIRCLES); /* dist pt to center */
  440.        if (dist_circle >= 0)
  441.           return 0.0; /* center ouside bounding sphere */
  442.        else
  443.           radius = min(radius, -dist_circle);
  444.        break;
  445.     }
  446.  
  447.     if (N == 0)
  448.         touched = TRUE; // first one's always OK
  449.     else
  450.         for (i=0, touched=FALSE; i < N; i++)
  451.         {
  452.             dist_circle = distance(x, y, z, i);
  453. #if defined (IBMPC_DISPLAY)
  454.             if(be_verbose) {
  455.                 GOTO_XY (1,2);
  456.                 printf("Test #%d\r",i);
  457.             }
  458.             GOTO_XY(1,3); printf("       \r"); // erase "Touched" message
  459. #endif
  460.             // find smallest radius so far
  461.             if (dist_circle <= radius)
  462.             {
  463.                 radius = dist_circle;
  464.                 touched = TRUE; // yes, we bumped into another circle
  465. #if defined (IBMPC_DISPLAY)
  466.                 if(be_verbose) {
  467.                     GOTO_XY(1,3);
  468.                     printf("Touched\r");
  469.                 }
  470. #endif
  471.             }
  472.             // if we're too small, return
  473.             if (radius < min_radius)
  474.                 return radius;
  475.         }
  476.  
  477.     if (touched)
  478.         return radius;
  479.     else
  480.         return 0.0; // if we didn't touch another circle, return as invalid
  481. } // new_radius
  482.  
  483.  
  484. /*-----------------------------------------------------------------------*/
  485. /*  Function: distance(x,y,z,i)
  486.         Purpose:
  487.         Returns the distance from the point x,y,z to edge of circle i.
  488.         Returns a negative value or zero distance if x,y is inside or on
  489.         circle i.
  490. */
  491. static DBL distance(DBL x, DBL y, DBL z, int i)
  492. {
  493.     DBL xdist, ydist, zdist, distancesq, dist;
  494.     xdist = x - c[i].x;
  495.     ydist = y - c[i].y;
  496.     zdist = z - c[i].z;
  497.     distancesq = (xdist*xdist) + (ydist*ydist) + (zdist*zdist);
  498.     dist = sqrt(distancesq) - c[i].r;
  499.     return (dist);
  500. } // distance
  501.  
  502.  
  503. /*-----------------------------------------------------------------------*/
  504. static void get_cmdline_args(int argc, char **argv)
  505. {
  506.     int        c;
  507.     char    filename[64];
  508.  
  509.     c = 1;
  510.     fpout = stdout;
  511.     while (c < argc)
  512.     {
  513.         if ((argv[c][0] == '-') || (argv[c][0] == '/'))
  514.         {
  515.             switch (argv[c][1])
  516.             {
  517.                 case 'o':    // -oOutputFileName
  518.                 case 'O':
  519.                     // get filename
  520.                     strcpy(filename,&argv[c][2]);
  521.                     // if no suffix, add one
  522.                     if (!strchr(filename,'.'))
  523.                         strcat(filename,".inc");
  524.                     if((fpout=fopen(filename,"w")) == NULL)
  525.                     {
  526.                         printf("Can't open %s ... aborting\n",filename);
  527.                         exit(1);
  528.                     }   
  529.                     break;
  530.  
  531.                 case 'n':    // -nNumSpheres
  532.                 case 'N':
  533.                     num_circles = atoi(&argv[c][2]);
  534.                     if ((num_circles <= MIN_CIRCLES) || (num_circles > MAX_CIRCLES))
  535.                         num_circles = MIN_CIRCLES;
  536.                     break;
  537.  
  538.                 case 'x':    // -xXregionOfSpheres
  539.                 case 'X':
  540.                     xmax = atoi(&argv[c][2]);
  541.                     if ((xmax < 0.0) || (xmax > 999.0))
  542.                         xmax = 100.0;
  543.                     break;
  544.  
  545.                 case 'y':    // -yYregionOfSpheres
  546.                 case 'Y':
  547.                     ymax = atoi(&argv[c][2]);
  548.                     if ((ymax < 0.0) || (ymax > 999.0))
  549.                         ymax = 100.0;
  550.                     break;
  551.  
  552.                 case 'z':    // -zZregionOfSpheres
  553.                 case 'Z':
  554.                     zmax = atoi(&argv[c][2]);
  555.                     if ((zmax < 0.0) || (zmax > 999.0))
  556.                         zmax = 0.0;
  557.                     break;
  558.  
  559.                 case 'r':    // -rRayTracerType (1=PoV 2.0, 2=PoV 1.0)
  560.                 case 'R':
  561.                     raytracer_kind = atoi(&argv[c][2]);
  562.                     if ((raytracer_kind < 1) || (raytracer_kind > RAY_MAX))
  563.                         raytracer_kind = POV20;
  564.                     break;
  565.  
  566.                 case 'a':    // -aMinRadius
  567.                 case 'A':
  568.                     min_radius = atof(&argv[c][2]);
  569.                     if ((min_radius <= MIN_RADIUS) || (min_radius > 10.0))
  570.                         min_radius = MIN_RADIUS;
  571.                     break;
  572.  
  573.                 case 'b':    // -bMaxRadius
  574.                 case 'B':
  575.                     max_radius = atof(&argv[c][2]);
  576.                     if ((max_radius <= 1.0) || (max_radius > MAX_RADIUS))
  577.                         max_radius = MAX_RADIUS;
  578.                     break;
  579.  
  580.                 case 'd':    // -dDistMethod (1=even, 2=Gauss5, 3=Gauss10)
  581.                 case 'D':
  582.                     distMethod = atoi(&argv[c][2]);
  583.                     if ((distMethod < 1) || (distMethod > DIST_MAX))
  584.                         distMethod = DIST_EVEN;
  585.                     break;
  586.  
  587.                 case 'k':    // -kBoundingKind
  588.                 case 'K':
  589.                     constrain = atoi(&argv[c][2]);
  590.                     if ((constrain < 1) || (constrain > NUM_BOUNDS))
  591.                         constrain = 1;
  592.                     break;
  593.  
  594.                 case 't':    // -tNumTextures
  595.                 case 'T':
  596.                     num_textures = atoi(&argv[c][2]);
  597.                     if ((num_textures < 1) || (num_textures > 100))
  598.                         num_textures = 1;
  599.                     break;
  600.  
  601.                 case 'v':    // -v  (Be Verbose)
  602.                 case 'V':
  603.                     be_verbose = TRUE;
  604.                     break;
  605.  
  606.                 default:
  607.                     printf("Error! Unrecognized parameter '%s', ignored.\n",argv[c]);
  608.                     break;
  609.             } // switch
  610.         }
  611.         else
  612.             printf("Error! Unrecognized parameter '%s', ignored.\n",argv[c]);
  613.  
  614.         /* go to next parameter */
  615.         c++; /* NOT! :-) [esp] */
  616.     }
  617. } // get_cmdline_args
  618.  
  619.  
  620. #if defined (IBMPC_DISPLAY)
  621.  
  622. /*-----------------------------------------------------------------------*/
  623. /*
  624.    Get parameters from user.
  625. */
  626. static void get_user_input()
  627. {
  628.     /* Get user input */
  629.     /* Note that the ranges here are pretty much arbitrary values at this
  630.        time. Need to test to establish good defaults and ranges. dmf */
  631.     int i;
  632.     char buf[200];
  633.  
  634.     /* center center boundaries to screen coordinates */
  635.     xmax = getmaxx();
  636.     ymax = getmaxy();
  637.     movetoxy(0, 0);
  638.     printf("\nPOV SUDS - Generate Osculating Objects\n\n");
  639.     printf("Press <Return> to keep same values\n");
  640.     printf("Current values shown in []\n");
  641.  
  642.     printf("\nEnter Ray Tracer\n");
  643.     for (i=0; i<RAY_MAX; i++)
  644.        printf("%-d=%s\n", i+1, tracers[i]);
  645.     printf("Choice [%-d] : ", raytracer_kind);
  646.     gets(buf);
  647.     sscanf(buf,"%d->", &raytracer_kind);
  648.     if(raytracer_kind==0) exit(0);
  649.     if ((raytracer_kind < 1) || (raytracer_kind > RAY_MAX))
  650.        raytracer_kind=POV20;
  651.  
  652.     printf("\nEnter Type of Bounds\n");
  653.     for (i=0; i<NUM_BOUNDS; i++)
  654.        printf("%-d=%s\n", i+1, bounds[i]);
  655.     printf("Choice [%-d] : ",constrain);
  656.     gets(buf);
  657.     sscanf(buf,"%d->",&constrain);
  658.     if ((constrain < 1) || (constrain > NUM_BOUNDS))
  659.        constrain = OLD;
  660.  
  661.     printf("\nRegion Containing Spheres:\n");
  662.     printf("   X from 0 to %-3g (fixed to screen resolution)\n",xmax);
  663.     printf("   Y from 0 to %-3g (fixed to screen resolution)\n",ymax);
  664.     printf("   Z from 0 to zmax (user set)\n");
  665.     printf("Enter zmax (depth of region ) [%-g] : ",zmax);
  666.     gets(buf);
  667.     sscanf(buf,"%lf->",&zmax);
  668.     if (zmax < 0)
  669.         zmax = 0;
  670.  
  671.  
  672.     CLEARDEVICE();
  673.     movetoxy(0,0);
  674.     printf("\nPOV SUDS - Generate Osculating Objects\n\n");
  675.     printf("Press <Return> to keep same values\n");
  676.     printf("Current values shown in []\n");
  677.  
  678.     printf("\nEnter Number Textures [%-d] : ",num_textures);
  679.     gets(buf);
  680.     sscanf(buf,"%d->",&num_textures);
  681.     if (num_textures < 1)
  682.        num_textures = 1;
  683.  
  684.     printf("\nEnter Max Spheres (%d-%d) [%-d] : ",
  685.         MIN_CIRCLES, MAX_CIRCLES,num_circles);
  686.     gets(buf);
  687.     sscanf(buf,"%d",&num_circles);
  688.     if (num_circles < MIN_CIRCLES)
  689.         num_circles = MIN_CIRCLES;
  690.     if (num_circles > MAX_CIRCLES)
  691.         num_circles = MAX_CIRCLES;
  692.  
  693.     printf("\nEnter Min Radius (0.01-10) [%-7.4lf] : ",min_radius);
  694.     gets(buf);
  695.     sscanf(buf,"%lf",&min_radius);
  696.     if (min_radius < MIN_RADIUS)
  697.         min_radius = MIN_RADIUS;
  698.     if (min_radius > 10.0)
  699.         min_radius = 10.0;
  700.  
  701.     printf("\nEnter Max Radius (1-100) [%-4lf] : ",max_radius);
  702.     gets(buf);
  703.     sscanf(buf,"%lf",&max_radius);
  704.     if (max_radius < min_radius)
  705.         max_radius = min_radius;
  706.     if (max_radius > MAX_RADIUS)
  707.         max_radius = MAX_RADIUS;
  708.  
  709.     printf("\nEnter Distribution Type\n");
  710.     for (i=0; i<DIST_MAX; i++)
  711.        printf("%-d=%s\n", i+1, disttypes[i]);
  712.     printf("Choice [%-d] : ", distMethod);
  713.     gets(buf);
  714.     sscanf(buf,"%d->", &distMethod);
  715.     if ((distMethod < 1) || (distMethod > DIST_MAX))
  716.         distMethod = DIST_EVEN;
  717.  
  718. } // get_user_input
  719.  
  720.  
  721. /*-----------------------------------------------------------------------*/
  722. static void exit_if_kbhit()
  723. {
  724.     if (kbhit())
  725.     {
  726.         getch();
  727.         closegraph();
  728.         exit(1);
  729.     }
  730. } // exit_if_kbhit
  731.  
  732.  
  733. /*-----------------------------------------------------------------------*/
  734. static void gerror(int errorcode)
  735. {
  736.     fprintf(stderr,"Graphics error: %s\n", grapherrormsg(errorcode));
  737.     fprintf(stderr,"Press any key to halt.");
  738.     getch();
  739.     exit(1);
  740. } // gerror
  741.  
  742.  
  743. /*-----------------------------------------------------------------------*/
  744. /* Uses the Borland BGI drivers */
  745. static void init_graphics (void)
  746. {
  747.     int gdriver = DETECT, gmode, errorcode;
  748.  
  749.     /* cut following 3 lines if you want to dynamically load egavga.bgi */
  750.  
  751.     errorcode = registerbgidriver(EGAVGA_driver);
  752.     if (errorcode < 0)
  753.         gerror(errorcode);
  754.  
  755.     initgraph(&gdriver, &gmode, "");
  756.     errorcode = graphresult();
  757.     if (errorcode != grOk)
  758.         gerror(errorcode);
  759. } // init_graphics
  760.  
  761.  
  762. /*-----------------------------------------------------------------------*/
  763. #define VIDEO 0x10
  764. static void movetoxy(int x, int y)
  765. {
  766.    union REGS regs;
  767.    regs.h.ah = 2;
  768.    regs.h.dh = y;
  769.    regs.h.dl = x;
  770.    regs.h.bh = 0;
  771.    int86(VIDEO,®s,®s);
  772. } // movetoxy
  773.  
  774.  
  775. /*-----------------------------------------------------------------------*/
  776. //Sort function for qsort
  777. int sort_function( const void *a, const void *b)
  778. {
  779.    if(a < b) return -1;
  780.    if(a > b) return  1;
  781.              return  0;
  782. }  // sort_function
  783.  
  784. #endif // IBMPC_DISPLAY
  785.  
  786.  
  787. /*-----------------------------------------------------------------------*/
  788. // Write an #include file for POV-Ray of a union of declared objects.
  789. // The composite is on the X/Y plane, left-handed coordinate system.
  790. // Uses a main datafile that defines "SudsObject" and associated textures
  791. // sudsTex1, sudsTex2...
  792. // Format:
  793. //
  794. //  (POV 1.0)
  795. //  #declare Suds = composite {
  796. //   object { SudsObject scale <x y z> translate <x y z> texture { sudsTex1 } }
  797. //   object { SudsObject scale <x y z> translate <x y z> texture { sudsTex2 } }
  798. //  }
  799. //
  800. //  (POV 2.0)
  801. //  #declare Suds = union {
  802. //   object { SudsObject scale <x, y, z> translate <x, y, z> texture { sudsTex1 } }
  803. //   object { SudsObject scale <x, y, z> translate <x, y, z> texture { sudsTex2 } }
  804. //  }
  805. //
  806. static void write_pov_data(void)
  807. {
  808.     int i,t;
  809.     DBL x_pos, y_pos, z_pos, radius;
  810.     DBL x_len, y_len, z_len;
  811.     DBL half_x, half_y, half_z;
  812.     DBL x_center, y_center, z_center;
  813.     DBL maxr_extent = 0.0;
  814.     DBL maxx_extent = 0.0;
  815.     DBL maxy_extent = 0.0;
  816.     DBL maxz_extent = 0.0;
  817.     DBL minr_extent = 1.7e+10;
  818.     DBL minx_extent = 1.7e+10;
  819.     DBL miny_extent = 1.7e+10;
  820.     DBL minz_extent = 1.7e+10;
  821.  
  822.     /* Write header to file */
  823.     fprintf(fpout,"%s  File generated with POV-Suds Utility (version %s) %s\n",
  824.             begComm[raytracer_kind-1], VERSION, endComm[raytracer_kind-1]);
  825.  
  826.     /* Write texture list to file */
  827.     write_pov_textures(num_textures);
  828.  
  829.     /* Write SudsObject template to file */
  830.     write_pov_sudsobj();
  831.  
  832.     /* Write the suds to file */
  833.     switch(raytracer_kind)
  834.     {
  835.     case POV10:
  836.         fprintf(fpout,"\n#declare Suds = composite {\n");
  837.         break;
  838.     case POV20:
  839.         fprintf(fpout,"\n#declare Suds = union {\n");
  840.         break;
  841.     }
  842.           
  843.     /* Write out each of the objects */
  844.     for (i=0; i<num_circles; i++)
  845.     {
  846.         x_pos  =   c[i].x;
  847.         y_pos  =   c[i].y;
  848.         z_pos  =   c[i].z;
  849.         radius =   c[i].r;
  850.         t = i % num_textures; // roll through the textures...
  851.  
  852.         write_pov_object(x_pos, y_pos, z_pos, radius, t);
  853.  
  854.         /* keep track of the overall extents */
  855.         maxx_extent = max(x_pos+radius, maxx_extent);
  856.         maxy_extent = max(y_pos+radius, maxy_extent);
  857.         maxz_extent = max(z_pos+radius, maxz_extent);
  858.         maxr_extent = max(radius, maxr_extent);
  859.  
  860.         minx_extent = min(x_pos-radius, minx_extent);
  861.         miny_extent = min(y_pos-radius, miny_extent);
  862.         minz_extent = min(z_pos-radius, minz_extent);
  863.         minr_extent = min(radius, minr_extent);
  864.     }
  865.  
  866.     /* Center the composite object, based on extents */
  867.     x_len = maxx_extent - minx_extent;
  868.     y_len = maxy_extent - miny_extent;
  869.     z_len = maxz_extent - minz_extent;
  870.  
  871.     half_x = x_len * 0.5;
  872.     half_y = y_len * 0.5;
  873.     half_z = z_len * 0.5;
  874.  
  875.     x_center = minx_extent + half_x;
  876.     y_center = miny_extent + half_y;
  877.     z_center = minz_extent + half_z;
  878.  
  879.     switch (raytracer_kind)
  880.     {
  881.     case POV10:
  882.        fprintf(fpout,"    translate <%g %g %g> %s Center it\n",
  883.                -x_center, -y_center, -z_center, begComm[raytracer_kind-1]);
  884.        fprintf(fpout,"} %s Suds composite\n", begComm[raytracer_kind-1]);
  885.        fprintf(fpout,"\n");
  886.        fprintf(fpout,"#declare Suds_Bounding_Box = box { <-1 -1 -1> <1 1 1>\n");
  887.        fprintf(fpout,"    scale <%g %g %g>\n", x_len, y_len, z_len);
  888.        fprintf(fpout,"} %s Suds_Bounding_Box\n", begComm[raytracer_kind-1]);
  889.        fprintf(fpout,"\n");
  890.        break;
  891.  
  892.     case POV20:
  893.        fprintf(fpout,"    translate <%g, %g, %g> %s Center it\n",
  894.                -x_center, -y_center, -z_center, begComm[raytracer_kind-1]);
  895.        fprintf(fpout,"%s  NOTE: In POV-Ray 2.0, auto-bounded!\n", begComm[raytracer_kind-1]);
  896.        fprintf(fpout,"} %s Suds union\n", begComm[raytracer_kind-1]);
  897.        fprintf(fpout,"\n");
  898.        break;
  899.     }
  900.  
  901.     fprintf(fpout,"%s  Object Extents: %s\n",
  902.             begComm[raytracer_kind-1], endComm[raytracer_kind-1]);
  903.  
  904.     fprintf(fpout,"%s    Min X = %7.2g,  Y = %7.2g,  Z = %7.2g,  R = %7.2g %s\n",
  905.             begComm[raytracer_kind-1], minx_extent, miny_extent, minz_extent, minr_extent, endComm[raytracer_kind-1]);
  906.  
  907.     fprintf(fpout,"%s    Max X = %7.2g,  Y = %7.2g,  Z = %7.2g,  R = %7.2g %s\n",
  908.             begComm[raytracer_kind-1], maxx_extent, maxy_extent, maxz_extent, maxr_extent, endComm[raytracer_kind-1]);
  909.  
  910.     fprintf(fpout,"%s  Objects generated : %5d %s\n",
  911.             begComm[raytracer_kind-1], N, endComm[raytracer_kind-1]);
  912.  
  913.     fprintf(fpout,"%s  Textures Needed   : %5d %s\n",
  914.             begComm[raytracer_kind-1], num_textures, endComm[raytracer_kind-1]);
  915.  
  916.     fprintf(fpout,"\n\n%s ------------------------------- %s\n",
  917.             begComm[raytracer_kind-1], endComm[raytracer_kind-1]);
  918.  
  919.     fprintf(fpout,"%s POV SUDS settings used to generate this file %s\n\n",
  920.             begComm[raytracer_kind-1], endComm[raytracer_kind-1]);
  921.     fprintf(fpout,"%s Ray Tracer         = %d (%s) %s\n",
  922.             begComm[raytracer_kind-1], raytracer_kind,tracers[raytracer_kind-1], endComm[raytracer_kind-1]);
  923.     fprintf(fpout,"%s Type of Bounds     = %d (%s) %s\n",
  924.             begComm[raytracer_kind-1], constrain,bounds[constrain-1], endComm[raytracer_kind-1]);
  925.     fprintf(fpout,"%s Region Containing Objects:%s\n",
  926.             begComm[raytracer_kind-1], endComm[raytracer_kind-1]);
  927.     fprintf(fpout,"%s    X from 0 to %7.2f (fixed to screen resolution) %s\n",
  928.             begComm[raytracer_kind-1], xmax, endComm[raytracer_kind-1]);
  929.     fprintf(fpout,"%s    Y from 0 to %7.2f (fixed to screen resolution) %s\n",
  930.             begComm[raytracer_kind-1], ymax, endComm[raytracer_kind-1]);
  931.     fprintf(fpout,"%s    Z from 0 to %7.2f (user setting of zmax) %s\n",
  932.             begComm[raytracer_kind-1], zmax, endComm[raytracer_kind-1]);
  933.     fprintf(fpout,"%s Number of Textures = %8d %s\n",
  934.             begComm[raytracer_kind-1], num_textures, endComm[raytracer_kind-1]);
  935.     fprintf(fpout,"%s Max Spheres        = %8d %s\n",
  936.             begComm[raytracer_kind-1], num_circles, endComm[raytracer_kind-1]);
  937.     fprintf(fpout,"%s Min Radius         = %8.4g %s\n",
  938.             begComm[raytracer_kind-1], min_radius, endComm[raytracer_kind-1]);
  939.     fprintf(fpout,"%s Max Radius         = %8.4g %s\n",
  940.             begComm[raytracer_kind-1], max_radius, endComm[raytracer_kind-1]);
  941. } // write_pov_data
  942.  
  943.  
  944. /*-----------------------------------------------------------------------*/
  945. /* Write a single circle out to PoV Ray format as a user-definable object */
  946. static void write_pov_object(DBL x, DBL y, DBL z, DBL r, int t)
  947. {
  948.     DBL x_scale = r;
  949.     DBL y_scale = x_scale;
  950.     DBL z_scale = x_scale;
  951.     DBL x_center = x;
  952.     DBL y_center = y;
  953.     DBL z_center = z;
  954.  
  955.     switch (raytracer_kind)
  956.     {
  957.     case POV10:
  958.        fprintf(fpout,
  959.        "    object { SudsObject scale <%8.4g %8.4g %8.4g> translate <%8.4g %8.4g %8.4g> texture {sudsTex%-2d} }\n",
  960.          x_scale, y_scale, z_scale, x_center, y_center, z_center, t+1);
  961.        break;
  962.  
  963.     case POV20:
  964.        fprintf(fpout,
  965.        "    object { SudsObject scale <%g, %g, %g> translate <%g, %g, %g> texture {sudsTex%-2d} }\n",
  966.          x_scale, y_scale, z_scale, x_center, y_center, z_center, t+1);
  967.        break;
  968.    }
  969. } // write_pov_object
  970.  
  971.  
  972. /*-----------------------------------------------------------------------*/
  973. /* Write an example SudsObject in PoV Ray format */
  974. static void write_pov_sudsobj(void)
  975. {
  976.     fprintf(fpout,  "\n/* --- Example Suds Object Declaration --- */\n\n");
  977.  
  978.     switch (raytracer_kind)
  979.     {
  980.     case POV10:
  981.        fprintf(fpout, "#declare SudsObject = object { sphere { <0 0 0> 1.0 } }\n");
  982.        break;
  983.  
  984.     case POV20:
  985.        fprintf(fpout, "#declare SudsObject = sphere { <0, 0, 0> 1.0 }\n");
  986.        break;
  987.    }
  988.  
  989.   fprintf(fpout,  "\n/* --- end of Suds Object Declaration */\n");
  990.  
  991. } // write_pov_sudsobj
  992.  
  993.  
  994. /*-----------------------------------------------------------------------*/
  995. /* Write a list of texture declarations in PoV Ray format */
  996. static void write_pov_textures(int num_tex)
  997. {
  998.     int t;
  999.  
  1000. fprintf(fpout,  "\n/* --- Example texture declarations, alter them to your liking --- */\n");
  1001.  
  1002. for (t=1; t<=num_tex; t++)
  1003. {
  1004.     fprintf(fpout, "\n");
  1005.     switch (raytracer_kind)
  1006.     {
  1007.     case POV10:
  1008.        fprintf(fpout, "#declare sudsTex%-2d = texture {\n", t);
  1009.        fprintf(fpout, "  color red %g green %g blue %g\n", EvenDistribution(), EvenDistribution(), EvenDistribution());
  1010.        fprintf(fpout, "  ambient 0.2 diffuse 0.7\n");
  1011.        fprintf(fpout, "  specular 0.8 roughness 0.01\n");
  1012.        fprintf(fpout, "} // sudsTex%-2d\n", t);
  1013.        break;
  1014.  
  1015.     case POV20:
  1016.        fprintf(fpout, "#declare sudsTex%-2d = texture {\n", t);
  1017.        fprintf(fpout, "  pigment {\n");
  1018.        fprintf(fpout, "    color rgb <%g,%g,%g>\n", EvenDistribution(), EvenDistribution(), EvenDistribution());
  1019.        fprintf(fpout, "  }\n");
  1020.        fprintf(fpout, "  // normal { wrinkles, bumps, etc. }\n");
  1021.        fprintf(fpout, "  finish {\n");
  1022.        fprintf(fpout, "    ambient 0.2 diffuse 0.7\n");
  1023.        fprintf(fpout, "    specular 0.8 roughness 0.01\n");
  1024.        fprintf(fpout, "  }\n");
  1025.        fprintf(fpout, "} // sudsTex%-2d\n", t);
  1026.        break;
  1027.    }
  1028. }
  1029.  
  1030. fprintf(fpout,  "\n/* --- end of texture declarations */\n");
  1031.  
  1032. } // write_pov_textures
  1033.  
  1034.  
  1035. /*-----------------------------------------------------------------------*/
  1036. static DBL GaussDistribution(int n)
  1037. {
  1038.     register    int    i;
  1039.     register    DBL    r = 0.0;
  1040.  
  1041.     // take n samples
  1042.     for (i=1; i <= n; i++) {
  1043.         r += (DBL)RAND();
  1044.     }
  1045.     // divide by n to get average
  1046.     r /= (DBL)n;
  1047.     // divide by max rand. # to get value between 0.0 and 1.0
  1048.     r /= (DBL)RAND_MAX;
  1049.     return (r);
  1050. } // GaussDistribution
  1051.  
  1052.  
  1053. /*-----------------------------------------------------------------------*/
  1054. static DBL EvenDistribution(void)
  1055. {
  1056.     register    DBL    r = 0.0;
  1057.  
  1058.     // divide by max rand. # to get value between 0.0 and 1.0
  1059.     r = (DBL)RAND() / (DBL)RAND_MAX;
  1060.     return (r);
  1061. } // EvenDistribution
  1062.  
  1063.  
  1064. /*-----------------------------------------------------------------------*/
  1065. // Returns a number between 0 and 1.
  1066. static DBL DoDistribution(void)
  1067. {
  1068.     register    DBL        r = 0.0;
  1069.  
  1070.     switch (distMethod)
  1071.     {
  1072.         case DIST_EVEN:
  1073.             r = EvenDistribution();
  1074.             break;
  1075.         case DIST_GAUSS5:
  1076.             r = GaussDistribution(5);
  1077.             break;
  1078.         case DIST_GAUSS10:
  1079.             r = GaussDistribution(10);
  1080.             break;
  1081.     }
  1082.     return r;
  1083. } // DoDistribution
  1084.